// Copyright (C) 2023 即时通讯网(52im.net) & Jack Jiang.
// The RainbowChat Project. All rights reserved.
// 
// 【本产品为著作权产品，合法授权后请放心使用，禁止外传！】
// 【本次授权给：<北京小羊驼科技有限公司>，授权编号：<NT231212144350>，代码指纹：<A.702363430.550>，技术对接人微信：<ID: wxid_wbyootvkdcgj22>】
// 【授权寄送：<收件：苏先生、地址：北京市大兴区北京密码西区6号楼1单元301、电话：18613885610、邮箱：bd@huiyuanxiang-inc.com>】
// 
// 【本系列产品在国家版权局的著作权登记信息如下】：
// 1）国家版权局登记名(简称)和权证号：RainbowChat    （证书号：软著登字第1220494号、登记号：2016SR041877）
// 2）国家版权局登记名(简称)和权证号：RainbowChat-Web（证书号：软著登字第3743440号、登记号：2019SR0322683）
// 3）国家版权局登记名(简称)和权证号：RainbowAV      （证书号：软著登字第2262004号、登记号：2017SR676720）
// 4）国家版权局登记名(简称)和权证号：MobileIMSDK-Web（证书号：软著登字第2262073号、登记号：2017SR676789）
// 5）国家版权局登记名(简称)和权证号：MobileIMSDK    （证书号：软著登字第1220581号、登记号：2016SR041964）
// * 著作权所有人：江顺/苏州网际时代信息科技有限公司
// 
// 【违法或违规使用投诉和举报方式】：
// 联系邮件：jack.jiang@52im.net
// 联系微信：hellojackjiang
// 联系QQ号：413980957
// 授权说明：http://www.52im.net/thread-1115-1-1.html
// 官方社区：http://www.52im.net
package com.x52im.rbchat.logic.chat_group.impl;

import android.os.Handler;

import java.util.Observer;

/**
 * 群聊时的发言太快检查和自动解禁实现类（本类已经高度封装，可重用于别的场景哦）.
 * <p>
 *     此类的作用跟手机qq群聊时防刷屏的机制是一样的，就是在用户在短时间内频繁发送消息
 *     时给于计数，并在超过阀值时判定为刷屏，那么界面UI上应该于以提示（比如qq里会
 *     提示"您发言太频繁，请休息后再试"这样的话数）。
 *	   <br>
 *     群聊是一个相当消耗服务端性能的系列功能，建议开发者一定要加上发言过快限制机制。
 *     而且从实际经验上来说，一个正常人没有任何理由需要在短时间内高频发消息，除非他有病。。。
 * </p>
 * 
 * @author Jack Jiang(http://www.52im.net/space-uid-1.html)
 * @since 2.5
 */
public class ProhibitSpeechChecker
{
	/** 首次启动时的默认时间戳：设置0就行了 */
	private final static int DEFAULT_INIT_TIMESTAMP = 0;
	/** 检查的时间跨度：目前定义的是6秒内连续发送超过3条就表示需要禁言 */
	public final static int TIME_INTERVAL = 6000;
	/** 连续发送的消息条数上限：目前定义的是6秒内连续发送超过3条就表示需要禁言 */
	public final static int MAX_SEND_COUNT = 3;
	
	/** 自动解禁：解禁剩余时间通知间隔（单位：毫秒） */
	public final static int AUTO_PROHIBIT_DELAY_TIME_WITH_MILLISECOND = 1000;
	/** 自动解禁：解禁剩余计数器 - 60即意味着通知60秒后即自动解禁  */
	public final static int AUTO_PROHIBIT_COUNTER_COUNT = 30;
	
	/** 通知观察者常量 - 通知类型：prohibitNow */
	public final static int OBSERVER_NOTIFY_TYPE_PROHIBITNOW = 0;
	/** 通知观察者常量 - 通知类型：unprohibitNow */
	public final static int OBSERVER_NOTIFY_TYPE_UNPROHIBITNOW = 1;
	/** 通知观察者常量 - 通知类型：prohobitRemain */
	public final static int OBSERVER_NOTIFY_TYPE_PROHOBITREMAIN = 2;
	
	// 时间检查起点（连续时间间隔没有超过6秒则不用更新本值（等到超过了这个时间再进入下一个判断区间），否则更新本值）
	private long lastSendTime = DEFAULT_INIT_TIMESTAMP;
	// 连续发送的消息条数
	private int accumulateCount = 0;
	// 当前是否处于禁言状态：true表示是，否则表示不是
	private boolean prohibit = false;
	// 禁言观察者对象（由外部传入）
	private Observer prohibitObserver = null;

	private String logTAG = "";
	
//	private static ProhibitSpeechChecker instance = null;
//
//	public static ProhibitSpeechChecker getInstance()
//	{
//		if(instance== null)
//			instance = new ProhibitSpeechChecker();
//		return instance;
//	}

	public ProhibitSpeechChecker(String logTAG)
	{
		this.logTAG = logTAG;
	}
	
	public Observer getProhibitObserver()
	{
		return prohibitObserver;
	}
	public void setProhibitObserver(Observer prohibitObserver)
	{
		this.prohibitObserver = prohibitObserver;
	}

	/**
	 * 更新检查标识.
	 * <p>
	 * 本方法在消息成功发送出去后被调用，以便置位相关标识.
	 */
	public void updateForSend()
	{
		long currentTimestamp = System.currentTimeMillis(); 
		System.out.println("["+logTAG+"]》》【禁言[检查]程序运行中】标识更新[前]，prohibit="+prohibit
				+", lastSendTime="+lastSendTime
				+", currentTimestamp="+currentTimestamp
				+", 时间差："+(currentTimestamp - lastSendTime)
				+", accumulateCount="+accumulateCount);
		// 如果时间检查起点被重置或首次启动时，直接累加发送次即可
		if(lastSendTime == DEFAULT_INIT_TIMESTAMP)
		{
			lastSendTime = System.currentTimeMillis();
			accumulateCount = 1;
		}
		// 距上1次的发送时间检查起点已经很长时间了，重置检查时间起点和相关标识即可
		else if(currentTimestamp - lastSendTime > TIME_INTERVAL)
		{
			lastSendTime = System.currentTimeMillis();
			accumulateCount = 1;
			prohibit = false;
		}
		// 距上次的时间起点在检查时间区间内：需要进一步进行连续消息发送次数的检查和判断
		else if(currentTimestamp - lastSendTime <= TIME_INTERVAL)
		{
			// 在期限内超过了发言条数上限
			if(accumulateCount >= MAX_SEND_COUNT - 1)
			{
				// 通知观察者：已被禁言
				notifyProhibitNow();
				// 标识禁言开启
				prohibit = true;
				// 禁言自动解禁线程启动
				new DelayedHandler(){
					// 禁言延迟完毕
					@Override
					protected void runOver()
					{
						System.out.println("["+logTAG+"]【禁言自动解禁程序运行中】已经解禁了（交给界面处理之！）....");
						// 重置检查时间起点
						lastSendTime = DEFAULT_INIT_TIMESTAMP;
						accumulateCount = 0;
						prohibit = false;
						// 通知观察者：已被解禁
						notifyUnprohibitNow();
					}
				};
			}
			// 没有超过发言上限
			else
			{
				// 连续消息条件加1
				accumulateCount += 1;
			}
		}
		System.out.println("["+logTAG+"]》》【禁言[检查]程序运行中】标识更新[后]，prohibit="+prohibit+", lastSendTime="+lastSendTime+", accumulateCount="+accumulateCount);
	}
	
	/**
	 * 当前是否处于禁言状态.
	 * 
	 * @return
	 */
	public boolean isProhibit()
	{
		return prohibit;
	}
	
	/**
	 * 通知观察者：已被禁言.
	 */
	private void notifyProhibitNow()
	{
		System.out.println("["+logTAG+"]【禁言[检查]程序运行中】您已被禁言了，禁言自动启动程序马上启动！");
		if(prohibitObserver != null)
			prohibitObserver.update(null, new int[]{OBSERVER_NOTIFY_TYPE_PROHIBITNOW, -1});
	}
	
	/**
	 * 通知观察者：已被解除禁言.
	 */
	private void notifyUnprohibitNow()
	{
		System.out.println("["+logTAG+"]【禁言[检查]程序运行中】禁言已经解除了！");
		if(prohibitObserver != null)
			prohibitObserver.update(null, new int[]{OBSERVER_NOTIFY_TYPE_UNPROHIBITNOW, -1});
	}
	
	/**
	 * 通知观察者：禁言解禁剩余时间.
	 * 
	 * @param remainSec 当前距离解禁还有多少秒
	 */
	protected void notifyProhobitRemain(int remainSec)
	{
		System.out.println("["+logTAG+"]【禁言自动解禁程序运行中】当前解禁倒计时："+remainSec);
		if(prohibitObserver != null)
			prohibitObserver.update(null, new int[]{OBSERVER_NOTIFY_TYPE_PROHOBITREMAIN, remainSec});
	}
	
	/**
	 * 自动解禁延迟实现类.
	 */
	private abstract class DelayedHandler extends Handler
	{
		// 解禁剩余计数器
		private int counter = AUTO_PROHIBIT_COUNTER_COUNT;
		
		private Runnable runnable = new Runnable(){
			@Override
			public void run()
			{
				if(counter > 0)
				{
					counter -= 1;
					notifyProhobitRemain(counter);
					postDelayed(runnable, AUTO_PROHIBIT_DELAY_TIME_WITH_MILLISECOND);
				}
				else
				{
					runOver();
				}
			}
		};

		/**
		 * 构造方法.
		 */
		public DelayedHandler()
		{
			this.postDelayed(runnable, AUTO_PROHIBIT_DELAY_TIME_WITH_MILLISECOND);
		}

		/**
		 * 延迟完成后要执行的方法.
		 */
		protected abstract void runOver();
		
		public void stop()
		{
			this.removeCallbacks(runnable);
		}
	}
}
